#include<stdio.h>
#include<stdlib.h>
#include<string.h>

// 辅助宏
#define Call_base(obj, fun, ...) (obj)->method->fun((obj), ##__VA_ARGS__)
#define Call(obj, fun, ...) (obj)->super.method->fun((obj), ##__VA_ARGS__)
#define New(type, ...) new_##type(__VA_ARGS__)
#define Delete_base(obj) (obj)->method->delete(obj)
#define Delete(obj) (obj)->super.method->delete(obj)

// 函数表
typedef struct Kemonomimi_fun_tbl{
    void (*show_ears)(void* _this); // 展示耳朵
    void (*speak)(void* _this);     // 说句话
    void (*info)(void* _this);      // 信息
    void (*delete)(void* _this);    // 析构函数
}Kemonomimi_fun_tbl;

// 基类 - 兽耳娘
typedef struct Kemonomimi{
    const Kemonomimi_fun_tbl* method; // 函数表
    char* name;                       // 名字
    char* fur_color;                  // 颜色
}Kemonomimi;

//子类 - 猫耳娘
typedef struct Nekomimi{
    Kemonomimi super;  // 父类
    char* like_food;   // 子类属性, 喜欢的食物
}Nekomimi;

//子类 - 犬耳娘
typedef struct Inumimi{
    Kemonomimi  super; // 父类
    char* hobby;       // 子类属性, 喜欢的事情
}Inumimi;


//基类方法实现
void Kemonomimi_show_ears(void* _this){
    Kemonomimi* self = _this;
    printf("%s 的兽耳轻轻抖动着...\n", self->name);
}

void Kemonomimi_info(void* _this){
    Kemonomimi* self = _this;
    printf("%s 是 %s 的~\n", self->name, self->fur_color);
}

void Kemonomimi_delete(void* _this){
    Kemonomimi* self = _this;
    free(self->name);
    free(self->fur_color);
    free(self);
}

// 基类函数表
static const Kemonomimi_fun_tbl Kemonomimi_method = {
    &Kemonomimi_show_ears,
    NULL,
    &Kemonomimi_info,
    &Kemonomimi_delete
};

// 基类构造函数
Kemonomimi* new_Kemonomimi(const char* name, const char* fur_color){
    Kemonomimi* obj = malloc(sizeof(Kemonomimi));
    obj->method = &Kemonomimi_method;
    obj->name = strdup(name);
    obj->fur_color = strdup(fur_color);
    return obj;
}

// 猫耳娘类方法实现
void Nekomimi_info(void* _this){
    Nekomimi* self = _this;
    Kemonomimi_info(self); //调用父类方法
    printf("而且喜欢吃 %s !!!\n", self->like_food);
}

void Nekomimi_speak(void* _this){
    Nekomimi* self = _this;
    printf("主人, 我饿了喵, 能不能给一些 %s 吃呢~ (ฅ^•ﻌ•^ฅ)\n", self->like_food);
}

void Nekomimi_delete(void* _this){
    Nekomimi* self = _this;
    free(self->like_food);
    Kemonomimi_delete(&(self->super));
}

// 猫耳娘类函数表
static const Kemonomimi_fun_tbl Nekomimi_method = {
    &Kemonomimi_show_ears,
    &Nekomimi_speak,
    &Nekomimi_info,
    &Nekomimi_delete
};

// 猫耳娘类构造函数
Nekomimi* new_Nekomimi(const char* name, const char* fur_color, const char* like_food){
    Nekomimi* obj = malloc(sizeof(Nekomimi));
    obj->super.method = &Nekomimi_method;
    obj->super.name = strdup(name);
    obj->super.fur_color = strdup(fur_color);
    obj->like_food = strdup(like_food);
    return obj;
}

// 犬耳娘类方法实现
void Inumimi_info(void* _this){
    Inumimi* self = _this;
    Kemonomimi_info(self);
    printf("而且喜欢玩 %s !!!\n", self->hobby);
}

void Inumimi_speak(void* _this){
    Inumimi* self = _this;
    printf("主人~今天天气不错~能和我一起 %s 吗~ (u･ω･u)\n", self->hobby);
}

void Inumimi_delete(void* _this){
    Inumimi* self = _this;
    free(self->hobby);
    Kemonomimi_delete(&(self->super));
}

// 犬耳娘类函数表
static const Kemonomimi_fun_tbl Inumimi_method = {
    &Kemonomimi_show_ears,
    &Inumimi_speak,
    &Inumimi_info,
    &Inumimi_delete
};

// 犬耳娘类构造函数
Inumimi* new_Inumimi(const char* name, const char* fur_color, const char* hobby){
    Inumimi* obj = malloc(sizeof(Inumimi));
    obj->super.method = &Inumimi_method;
    obj->super.name = strdup(name);
    obj->super.fur_color = strdup(fur_color);
    obj->hobby = strdup(hobby);
    return obj;
}

int main()
{
    // 创建子类对象并调用方法 "封装和继承"
    Nekomimi* obj_cat = New(Nekomimi, "总之就是不会取名字", "白色", "蜂蜜水");
    Inumimi* obj_dog = New(Inumimi, "确实取不明白", "金色", "调教");
    Call(obj_cat, show_ears);
    Call(obj_cat, speak);
    Call(obj_cat, info);
    Call(obj_dog, show_ears);
    Call(obj_dog, speak);
    Call(obj_dog, info);
    
    // 用父类指针调子类的方法什么的也是可以的 "多态"
    Kemonomimi* tmp = obj_cat;
    Call_base(tmp, speak);
    tmp = obj_dog;
    Call_base(tmp, speak);
    
    // 销毁对象
    Delete(obj_cat);
    Delete(obj_dog);
    return 0;
}